//----------------------------------------------------
//--                                                --
//--               www.riemers.net                  --
//--         Series 4: Advanced terrain             --
//--                 Shader code                    --
//--                                                --
//----------------------------------------------------

//------- Constants --------
float4x4 xView;
float4x4 xProjection;
float4x4 xWorld;
float4x4 xReflectionView;

float3 xCamPos;

float3 xLightDirection;
float xAmbient;
bool xEnableLighting;
bool xEnableClipPlane0;
float4 ClipPlane0; 

float xWaveLength;
float xWaveHeight;

float xTime;
float3 xWindDirection;
float xWindForce;

float xOvercast;




//------- Texture Samplers --------
Texture xTexture0;
sampler TextureSampler0 = sampler_state { texture = <xTexture0> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = wrap; AddressV = wrap;};

Texture xTexture1;
sampler TextureSampler1 = sampler_state { texture = <xTexture1> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = wrap; AddressV = wrap;};

Texture xTexture2;
sampler TextureSampler2 = sampler_state { texture = <xTexture2> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};

Texture xTexture3;
sampler TextureSampler3 = sampler_state { texture = <xTexture3> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};

Texture xReflectionMap;
sampler ReflectionSampler = sampler_state { texture = <xReflectionMap> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};

Texture xRefractionMap;
sampler RefractionSampler = sampler_state { texture = <xRefractionMap> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};

Texture xWaterBumpMap;
sampler WaterBumpMapSampler = sampler_state { texture = <xWaterBumpMap> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};


//------- Technique: Pretransformed --------

struct VertexToPixel
{
    float4 Position   	: POSITION;    
    float4 Color		: COLOR0;
    float LightingFactor: TEXCOORD0;
    float2 TextureCoords: TEXCOORD1;
};

struct PixelToFrame
{
    float4 Color : COLOR0;
};

VertexToPixel PretransformedVS( float4 inPos : POSITION, float4 inColor: COLOR)
{	
	VertexToPixel Output = (VertexToPixel)0;
	float4x4 preViewProjection = mul (xView, xProjection);
	float4x4 preWorldViewProjection = mul (xWorld, preViewProjection);
    
	Output.Position = mul(inPos, preWorldViewProjection);
	Output.Color = inColor;
    
	return Output;    
}

PixelToFrame PretransformedPS(VertexToPixel PSIn) 
{
	PixelToFrame Output = (PixelToFrame)0;		
	
	Output.Color = PSIn.Color;

	return Output;
}

technique Pretransformed
{
	pass Pass0
	{   
		VertexShader = compile vs_2_0 PretransformedVS();
		PixelShader  = compile ps_2_0 PretransformedPS();
	}
}

//------- Technique: Textured --------
struct TexVertexToPixel
{
    float4	Position		: POSITION;   
    float2	TextureCoords	: TEXCOORD1;
	float	clipDistances	: TEXCOORD2;
	float3	Normal			: TEXCOORD3;
	float4 Position3D       : TEXCOORD4;
};

struct TexPixelToFrame
{
    float4 Color : COLOR0;
};

TexVertexToPixel TexturedVS( float4 inPos : POSITION, float3 inNormal: NORMAL, float2 inTexCoords: TEXCOORD0)
{	
	TexVertexToPixel Output = (TexVertexToPixel)0;
	float4x4 preViewProjection = mul (xView, xProjection);
	float4x4 preWorldViewProjection = mul (xWorld, preViewProjection);
    
	Output.Position = mul(inPos, preWorldViewProjection);	
	Output.TextureCoords = inTexCoords;
	
	Output.Normal = normalize(mul(normalize(inNormal), xWorld));	
	
	
    Output.Position3D = mul(inPos, xWorld);     
	Output.clipDistances = dot( mul(inPos,xWorld) , ClipPlane0); 

	return Output;     
}

TexPixelToFrame TexturedPS(TexVertexToPixel PSIn) 
{
	if(xEnableClipPlane0)
		clip(PSIn.clipDistances);

	TexPixelToFrame Output = (TexPixelToFrame)0;		
    
	Output.Color = tex2D(TextureSampler0, PSIn.TextureCoords);

	float3 Normal = tex2D(TextureSampler1, PSIn.TextureCoords);
	Normal = normalize((Normal-0.5)*2);
	Normal = normalize((Normal)+PSIn.Normal);

	float3 eyeVector = normalize(xCamPos - PSIn.Position3D);

	float LightingFactor = 1;
	if (xEnableLighting)
		LightingFactor = saturate(dot(Normal, -xLightDirection)*0.12);

	float Diff = -saturate(dot(Normal, xLightDirection)); 
	float3 Reflect = normalize(( 2 * Diff * Normal) -xLightDirection);  
	float Specular = pow(saturate(dot(Reflect, eyeVector)), 5);

	Output.Color.rgb *= saturate(LightingFactor + xAmbient);
	//Output.Color.rgb += Specular;
	return Output;
}

technique Textured
{
	pass Pass0
    {   
    	VertexShader = compile vs_2_0 TexturedVS();
        PixelShader  = compile ps_2_0 TexturedPS();
    }
}


//------- Technique: MultiTextured --------


struct MTVertexToPixel
{
    float4 Position			: POSITION;    
    float4 Color            : COLOR0;
    float3 Normal           : TEXCOORD0;
    float2 TextureCoords    : TEXCOORD1;
    float4 LightDirection   : TEXCOORD2;
    float4 TextureWeights   : TEXCOORD3;
	float  Depth            : TEXCOORD4;
	float clipDistances		: TEXCOORD5;
};

struct MTPixelToFrame
{
    float4 Color : COLOR0;
};

MTVertexToPixel MultiTexturedVS( float4 inPos : POSITION, float3 inNormal: NORMAL, float2 inTexCoords: TEXCOORD0, float4 inTexWeights: TEXCOORD1)
{   
    MTVertexToPixel Output = (MTVertexToPixel)0;
    float4x4 preViewProjection = mul (xView, xProjection);
    float4x4 preWorldViewProjection = mul (xWorld, preViewProjection);

	Output.clipDistances = dot(inPos, ClipPlane0); 
    
    Output.Position = mul(inPos, preWorldViewProjection);
    Output.Normal = mul(normalize(inNormal), xWorld);
    Output.TextureCoords = inTexCoords;
    Output.LightDirection.xyz = -xLightDirection;
    Output.LightDirection.w = 1;    
    Output.TextureWeights = inTexWeights;
	Output.Depth = Output.Position.z/Output.Position.w;
    
    return Output;    
}

MTPixelToFrame MultiTexturedPS(MTVertexToPixel PSIn)
{
	if(xEnableClipPlane0)
		clip(PSIn.clipDistances);

    MTPixelToFrame Output = (MTPixelToFrame)0;        
	float blendDistance = 0.98f;
	float blendWidth = 0.01f;
	float blendFactor = clamp((PSIn.Depth-blendDistance)/blendWidth, 0, 1);
    
	
	

	float4 farColor;
	farColor = tex2D(TextureSampler0, PSIn.TextureCoords)*PSIn.TextureWeights.x;
	farColor += tex2D(TextureSampler1, PSIn.TextureCoords)*PSIn.TextureWeights.y;
	farColor += tex2D(TextureSampler2, PSIn.TextureCoords)*PSIn.TextureWeights.z;
	farColor += tex2D(TextureSampler3, PSIn.TextureCoords)*PSIn.TextureWeights.w;
	//farColor = 0.2;
	//farColor.r =1;
    
	float4 nearColor;
	float2 nearTextureCoords = PSIn.TextureCoords*4;
	nearColor = tex2D(TextureSampler0, nearTextureCoords)*PSIn.TextureWeights.x;
	nearColor += tex2D(TextureSampler1, nearTextureCoords)*PSIn.TextureWeights.y;
	nearColor += tex2D(TextureSampler2, nearTextureCoords)*PSIn.TextureWeights.z;
	nearColor += tex2D(TextureSampler3, nearTextureCoords)*PSIn.TextureWeights.w;
	//nearColor = 0;
	//nearColor.b = 1;

    float lightingFactor = 1;
    if (xEnableLighting)
        lightingFactor = saturate(saturate(dot(PSIn.Normal, PSIn.LightDirection)) + xAmbient);

	
	Output.Color = lerp(nearColor, farColor, blendFactor);;  
	Output.Color *= lightingFactor;

    return Output;
}

technique MultiTextured
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 MultiTexturedVS();
        PixelShader = compile ps_2_0 MultiTexturedPS();
    }
}

//------- Technique: clipingplane --------


 

 
void vs(inout float4 position : POSITION0, out float4 clipDistances : TEXCOORD0) 
{ 
    clipDistances.x = dot(position, ClipPlane0); 
    clipDistances.y = 0; 
    clipDistances.z = 0; 
    clipDistances.w = 0; 
 
    position = mul(mul(mul(position, xWorld), xView), xProjection); 

	
} 
 
float4 ps(float4 clipDistances : TEXCOORD0) : COLOR0 
{ 
    clip(clipDistances); 
 
    return float4(clipDistances); // TODO: whatever other shading logic you want 
} 
 
technique clipingplane
{ 
    pass 
    { 
        VertexShader = compile vs_2_0 vs(); 
        PixelShader = compile ps_2_0 ps(); 
    } 
} 

//------- Technique: Water --------
struct WVertexToPixel
{
    float4 Position                 : POSITION;
    float4 ReflectionMapSamplingPos    : TEXCOORD1;
    float2 BumpMapSamplingPos        : TEXCOORD2;
    float4 RefractionMapSamplingPos : TEXCOORD3;
    float4 Position3D                : TEXCOORD4;
};

struct WPixelToFrame
{
    float4 Color : COLOR0;
};

WVertexToPixel WaterVS(float4 inPos : POSITION, float2 inTex: TEXCOORD)
{    
	WVertexToPixel Output = (WVertexToPixel)0;

	float4x4 preViewProjection = mul (xView, xProjection);
	float4x4 preWorldViewProjection = mul (xWorld, preViewProjection);
	float4x4 preReflectionViewProjection = mul (xReflectionView, xProjection);
	float4x4 preWorldReflectionViewProjection = mul (xWorld, preReflectionViewProjection);

	Output.Position = mul(inPos, preWorldViewProjection);
	Output.ReflectionMapSamplingPos = mul(inPos, preWorldReflectionViewProjection);    
	Output.RefractionMapSamplingPos = mul(inPos, preWorldViewProjection);
	Output.Position3D = mul(inPos, xWorld);        

     
	float3 windDir = normalize(xWindDirection);    
	float3 perpDir = cross(xWindDirection, float3(0,1,0));
	float ydot = dot(inTex, xWindDirection.xz);
	float xdot = dot(inTex, perpDir.xz);
	float2 moveVector = float2(xdot, ydot);
	moveVector.y += xTime*xWindForce;    
	//Output.BumpMapSamplingPos = moveVector/xWaveLength;   

    Output.BumpMapSamplingPos = inTex;
    return Output;
}

WPixelToFrame WaterPS(WVertexToPixel PSIn)
{
	
    WPixelToFrame Output = (WPixelToFrame)0;     
	   
	float2 ProjectedTexCoords;

    ProjectedTexCoords.x = PSIn.ReflectionMapSamplingPos.x/PSIn.ReflectionMapSamplingPos.w/2.0f + 0.5f;
    ProjectedTexCoords.y = -PSIn.ReflectionMapSamplingPos.y/PSIn.ReflectionMapSamplingPos.w/2.0f + 0.5f;   
	 
    float4 bumpColor = tex2D(WaterBumpMapSampler, PSIn.BumpMapSamplingPos);
	float2 perturbation = xWaveHeight*(bumpColor.rg - 0.5f)*2.0f;
	float2 perturbatedTexCoords = ProjectedTexCoords + perturbation;

    float4 reflectiveColor = tex2D(ReflectionSampler, perturbatedTexCoords);  
    
	float2 ProjectedRefrTexCoords;
	ProjectedRefrTexCoords.x = PSIn.RefractionMapSamplingPos.x/PSIn.RefractionMapSamplingPos.w/2.0f + 0.5f;
	ProjectedRefrTexCoords.y = -PSIn.RefractionMapSamplingPos.y/PSIn.RefractionMapSamplingPos.w/2.0f + 0.5f;    
	float2 perturbatedRefrTexCoords = ProjectedRefrTexCoords + perturbation;    
	float4 refractiveColor = tex2D(RefractionSampler, perturbatedRefrTexCoords);

	float3 eyeVector = normalize(xCamPos - PSIn.Position3D);

	float3 normalVector = (bumpColor.rbg-0.5f)*2.0f;

	float fresnelTerm = dot(eyeVector, normalVector);
	
	float4 combinedColor = lerp(reflectiveColor, refractiveColor, fresnelTerm);

	float4 dullColor = float4(0.3f, 0.3f, 0.5f, 1.0f);

	float3 reflectionVector = -reflect(xLightDirection, normalVector);
	
	//highlight
	Output.Color = lerp(combinedColor, dullColor, 0.2f);
	float specular = dot(normalize(reflectionVector), normalize(eyeVector));
	specular = pow(specular, 256);        
	Output.Color.rgb += specular;

	Output.Color = reflectiveColor;

    return Output;
}

technique Water
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 WaterVS();
        PixelShader = compile ps_2_0 WaterPS();
    }
}

//------- Technique: PerlinNoise --------
struct PNVertexToPixel
{    
    float4 Position         : POSITION;
    float2 TextureCoords    : TEXCOORD0;
};

struct PNPixelToFrame
{
    float4 Color : COLOR0;
};

PNVertexToPixel PerlinVS(float4 inPos : POSITION, float2 inTexCoords: TEXCOORD)
{    
    PNVertexToPixel Output = (PNVertexToPixel)0;
    
    Output.Position = inPos;
    Output.TextureCoords = inTexCoords;
    
    return Output;    
}

PNPixelToFrame PerlinPS(PNVertexToPixel PSIn)
{
    PNPixelToFrame Output = (PNPixelToFrame)0;    
    
    float2 move = float2(0,1);
    float4 perlin = tex2D(TextureSampler0, (PSIn.TextureCoords)+xTime*move)/2;
    perlin += tex2D(TextureSampler0, (PSIn.TextureCoords)*2+xTime*move)/4;
    perlin += tex2D(TextureSampler0, (PSIn.TextureCoords)*4+xTime*move)/8;
    perlin += tex2D(TextureSampler0, (PSIn.TextureCoords)*8-xTime*move)/16;
    perlin += tex2D(TextureSampler0, (PSIn.TextureCoords)*16+xTime*move)/32;
    perlin += tex2D(TextureSampler0, (PSIn.TextureCoords)*32+xTime*move)/32;
	
	Output.Color = 1.0f-pow(perlin, xOvercast)*2.0f;
	return Output;    
}

technique PerlinNoise
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 PerlinVS();
        PixelShader = compile ps_2_0 PerlinPS();
    }
}

//------- Technique: SkyDome --------
struct SDVertexToPixel
{    
    float4 Position         : POSITION;
    float2 TextureCoords    : TEXCOORD0;
    float4 ObjectPosition    : TEXCOORD1;
};

struct SDPixelToFrame
{
    float4 Color : COLOR0;
};

SDVertexToPixel SkyDomeVS( float4 inPos : POSITION, float2 inTexCoords: TEXCOORD0)
{    
    SDVertexToPixel Output = (SDVertexToPixel)0;
    float4x4 preViewProjection = mul (xView, xProjection);
    float4x4 preWorldViewProjection = mul (xWorld, preViewProjection);
    
    Output.Position = mul(inPos, preWorldViewProjection);
    Output.TextureCoords = inTexCoords;
    Output. ObjectPosition = inPos;
    
    return Output;    
}

SDPixelToFrame SkyDomePS(SDVertexToPixel PSIn)
{
    SDPixelToFrame Output = (SDPixelToFrame)0;        

    float4 topColor = float4(0.3f, 0.3f, 0.8f, 1);    
    float4 bottomColor = 1;    
    
    float4 baseColor = lerp(bottomColor, topColor, saturate((PSIn. ObjectPosition.y)/0.4f));
    float4 cloudValue = tex2D(TextureSampler0, PSIn.TextureCoords).r;
    
    Output.Color = lerp(baseColor,1, cloudValue);        

    return Output;
}

technique SkyDome
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 SkyDomeVS();
        PixelShader = compile ps_2_0 SkyDomePS();
    }
}

//------- Technique: watertest --------
struct tPNVertexToPixel
{    
    float4 Position         : POSITION;
    float2 TextureCoords    : TEXCOORD0;
};

struct tPNPixelToFrame
{
    float4 Color : COLOR0;
};

tPNVertexToPixel tPerlinVS(float4 inPos : POSITION, float2 inTexCoords: TEXCOORD)
{    
    tPNVertexToPixel Output = (PNVertexToPixel)0;
    
    Output.Position = inPos;
    Output.TextureCoords = inTexCoords;
    
    return Output;    
}

tPNPixelToFrame tPerlinPS(PNVertexToPixel PSIn)
{
    tPNPixelToFrame Output = (PNPixelToFrame)0;    
    
    float2 move = float2(0,1);
    float4 perlin = tex2D(TextureSampler0, PSIn.TextureCoords);
	
	Output.Color = perlin;
	return Output;    
}

technique watertest
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 tPerlinVS();
        PixelShader = compile ps_2_0 tPerlinPS();
    }
}